package com.dlc.apk.util;


import android.os.Environment;
import android.util.Log;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class LogPlus {

    private static int sCurrentLogLevel = Log.DEBUG;
    private static String sPrefix = null;

    public static ILogListener getLogListener() {
        return logListener;
    }

    public static void setLogListener(ILogListener logListener) {
        LogPlus.logListener = logListener;
    }

    @IntDef({Log.VERBOSE, Log.DEBUG, Log.INFO, Log.WARN, Log.ERROR})
    @Retention(RetentionPolicy.SOURCE)
    public @interface LogLevel {
    }

    /**
     * 初始化LogPlus，可选
     *
     * @param prefix   Tag前缀，可以防止 RIL，IMS，AT，GSM，STK，CDMA，SMS 开头的log不会被打印
     * @param logLevel 打log等级
     */
    public static void init(String prefix, @LogLevel int logLevel) {
        if (prefix != null) {
            prefix = prefix.trim();
            if (prefix.length() > 0) {
                sPrefix = prefix;
            }
        }
        sCurrentLogLevel = logLevel;
    }

    public static void v(@Nullable String tag, String msg, @Nullable Throwable tr) {
        log(Log.VERBOSE, tag, msg, tr);
    }

    public static void v(@Nullable String tag, String msg) {
        log(Log.VERBOSE, tag, msg, null);
    }

    public static void v(String msg, @Nullable Throwable tr) {
        log(Log.VERBOSE, null, msg, tr);
    }

    public static void v(String msg) {
        log(Log.VERBOSE, null, msg, null);
    }

    public static void d(@Nullable String tag, String msg, @Nullable Throwable tr) {
        log(Log.DEBUG, tag, msg, tr);
    }

    public static void d(@Nullable String tag, String msg) {
        log(Log.DEBUG, tag, msg, null);
    }

    public static void d(String msg, @Nullable Throwable tr) {
        log(Log.DEBUG, null, msg, tr);
    }

    public static void d(String msg) {
        log(Log.DEBUG, null, msg, null);
    }

    public static void i(@Nullable String tag, String msg, @Nullable Throwable tr) {
        log(Log.INFO, tag, msg, tr);
    }

    public static void i(@Nullable String tag, String msg) {
        log(Log.INFO, tag, msg, null);
    }

    public static void i(String msg, @Nullable Throwable tr) {
        log(Log.INFO, null, msg, tr);
    }

    public static void i(String msg) {
        log(Log.INFO, null, msg, null);
    }

    public static void w(@Nullable String tag, String msg, @Nullable Throwable tr) {
        log(Log.WARN, tag, msg, tr);
    }

    public static void w(@Nullable String tag, String msg) {
        log(Log.WARN, tag, msg, null);
    }

    public static void w(String msg, @Nullable Throwable tr) {
        log(Log.WARN, null, msg, tr);
    }

    public static void w(String msg) {
        log(Log.WARN, null, msg, null);
    }

    public static void e(@Nullable String tag, String msg, @Nullable Throwable tr) {
        log(Log.ERROR, tag, msg, tr);
    }

    public static void e(@Nullable String tag, String msg) {
        log(Log.ERROR, tag, msg, null);
    }

    public static void e(String msg, @Nullable Throwable tr) {
        log(Log.ERROR, null, msg, tr);
    }

    public static void e(String msg) {
        log(Log.ERROR, null, msg, null);
    }

    private static void log(int logLevel, String tag, String msg, Throwable tr) {
        if (logLevel < sCurrentLogLevel) {
            return;
        }
        StackTraceElement e = Thread.currentThread().getStackTrace()[4];
        String fileName = e.getFileName();
        int lineNum = e.getLineNumber();
        String methodName = e.getMethodName();

        StringBuilder sb = new StringBuilder();

        sb.append(methodName)
                .append('(')
                .append(fileName)
                .append(':')
                .append(lineNum)
                .append(')')
                .append(msg);

        msg = sb.toString();

        sb.delete(0, sb.length());

        // 注意，如果tag是"IMS"开头的(还有其他)，log会不打印，所以加个"前缀_"
        // 参考这里 http://stackoverflow.com/a/36469141/5324526
        if (sPrefix != null) {
            sb.append(sPrefix).append('_');
        }

        if (tag == null || tag.length() == 0 || tag.trim().length() == 0) {
            String className = e.getClassName();
            int index = className.lastIndexOf('.') + 1;
            sb.append(className, index, className.length());
        } else {
            sb.append(tag);
        }

        tag = sb.toString();

        if (tr == null) {
            switch (logLevel) {
                case Log.VERBOSE:
                    Log.v(tag, msg);
                    break;
                case Log.DEBUG:
                    Log.d(tag, msg);
                    break;
                case Log.INFO:
                    Log.i(tag, msg);
                    break;
                case Log.WARN:
                    Log.w(tag, msg);
                    break;
                case Log.ERROR:
                    Log.e(tag, msg);
                    break;
            }
        } else {
            switch (logLevel) {
                case Log.VERBOSE:
                    Log.v(tag, msg, tr);
                    break;
                case Log.DEBUG:
                    Log.d(tag, msg, tr);
                    break;
                case Log.INFO:
                    Log.i(tag, msg, tr);
                    break;
                case Log.WARN:
                    Log.w(tag, msg, tr);
                    break;
                case Log.ERROR:
                    Log.e(tag, msg, tr);
                    break;
            }
        }

        if (isDebug) {
            /*todo 保存日志到本地*/
            String finalTag = tag;
            String finalMsg = msg;
            threadPoolExecutor.execute(() -> {
                deleteYesterdayLogFile();
                saveLogToFile(finalTag, finalMsg, tr);
            });

            if (logListener != null) {
                logListener.onLog(finalTag, msg, tr);
            }
        }
    }

    private static Executor threadPoolExecutor = Executors.newFixedThreadPool(3);

    private static void saveLogToFile(String tag, String msg, Throwable th) {
        File logFile = getTodayLogFile();
        if (logFile == null) {
            return;
        }
        //e("###日志文件路径:" + logFile.getAbsolutePath());
        MultiSimpleDateFormat sdf = threadLocal.get();
        if (sdf == null) {
            threadLocal.set(new MultiSimpleDateFormat(new SimpleDateFormat("yyyyMMdd"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")));
        }
        sdf = threadLocal.get();
        if (sdf == null) {
            return;
        }

        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(logFile, true);
            String log = String.format("[ %s ] %s ========> %s\n", sdf.yyyyMMddHHmmssSSS.format(new Date()), tag, msg);
            fileOutputStream.write(log.getBytes());
            if (th != null) {
                fileOutputStream.write(getStackTraceInfo(th).getBytes());
            }
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void deleteYesterdayLogFile() {
        try {
            File yesterdayLogFile = getYesterdayLogFile();
            if (yesterdayLogFile != null && yesterdayLogFile.exists()) {
                yesterdayLogFile.delete();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }


    private static boolean isDebug;
    private static String logFileName;

    public static void setDebug(boolean debug, String logFileName) {
        isDebug = debug;
        LogPlus.logFileName = logFileName;
    }

    public static File getTodayLogFile() {
        MultiSimpleDateFormat sdf = threadLocal.get();
        if (sdf == null) {
            threadLocal.set(new MultiSimpleDateFormat(new SimpleDateFormat("yyyyMMdd"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")));
        }
        sdf = threadLocal.get();
        if (sdf == null) {
            return null;
        }
        return new File(String.format("%s/%s_%s.log", Environment.getExternalStorageDirectory().toString(), logFileName, sdf.yyyyMMdd.format(new Date())));
    }

    public static File getLogFile(String date) {
        return new File(String.format("%s/%s_%s.log", Environment.getExternalStorageDirectory().toString(), logFileName, date));
    }

    public static File getYesterdayLogFile() {
        MultiSimpleDateFormat sdf = threadLocal.get();
        if (sdf == null) {
            threadLocal.set(new MultiSimpleDateFormat(new SimpleDateFormat("yyyyMMdd"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")));
        }
        sdf = threadLocal.get();
        if (sdf == null) {
            return null;
        }
        return new File(String.format("%s/%s_%s.log", Environment.getExternalStorageDirectory().toString(), logFileName, sdf.yyyyMMdd.format(new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24))));
    }


    private static ThreadLocal<MultiSimpleDateFormat> threadLocal = new ThreadLocal<>();

    private static class MultiSimpleDateFormat {
        public SimpleDateFormat yyyyMMdd;
        public SimpleDateFormat yyyyMMddHHmmssSSS;

        public MultiSimpleDateFormat(SimpleDateFormat yyyyMMdd, SimpleDateFormat yyyyMMddHHmmssSSS) {
            this.yyyyMMdd = yyyyMMdd;
            this.yyyyMMddHHmmssSSS = yyyyMMddHHmmssSSS;
        }
    }

    public static String getStackTraceInfo(Throwable throwable) {
        if (throwable == null) {
            return "";
        }
        PrintWriter pw;
        StringWriter writer = new StringWriter();
        try {
            pw = new PrintWriter(writer);
            throwable.printStackTrace(pw);
        } catch (Exception ex) {

        }
        return writer.toString();
    }

    private static ILogListener logListener;

    public interface ILogListener {
        void onLog(String tag, String log, Throwable th);
    }
}
